package dk.aiszone.lang.sql;

import dk.aiszone.lang.sql.SQLResult.UnknownIdException;
import dk.aiszone.lang.sql.interfaces.SQLConstraint;
import dk.aiszone.lang.sql.interfaces.SQLPart;

/**
 * Representation of the <i>Insert</i> SQL statement.
 * 
 * @author Ais
 */
public class SQLUpdate extends AbstractSQLObject {

    private SQLAnd basisAndStatement;
    private SQLEqualsList basisEqualsList;

    private SQLAnd currentAndStatement;
    private SQLEqualsList currentEqualsList;

    private SQLResult prepared = null;
    private final SQLTable table;

    /**
     * Constructor for the <code>SQLUpdate</code> object
     * 
     * @param table
     *            The <code>SQLTable</code> to insert data into.
     */
    public SQLUpdate(SQLTable table) {
        this.table = table;
    }

    /**
     * Add a constraint to the <code>SQLUpdate</code> object.
     * 
     * @param constraint
     *            The <code>SQLConstratint</code> to add.
     */
    public void addConstraint(SQLConstraint constraint) {
        if (currentAndStatement == null) {
            currentAndStatement = new SQLAnd(constraint);
            basisAndStatement = currentAndStatement;
        } else {
            SQLAnd tmp = new SQLAnd(constraint);
            currentAndStatement.setConstraint2(tmp);
            currentAndStatement = tmp;
        }
    }

    /**
     * Add a <code>SQLOr</code> to the <code>SQLSelect</code> statement.
     * 
     * @param or
     *            The <code>SQLOr</code> to be added.
     */
    public void addConstraint(SQLOr or) {
        if (currentAndStatement == null) {
            currentAndStatement = new SQLAnd(or);
            basisAndStatement = currentAndStatement;
        } else {
            SQLAnd tmp = new SQLAnd(or);
            currentAndStatement.setConstraint2(tmp);
            currentAndStatement = tmp;
        }
    }

    /**
     * Add a <code>SQLAssignableField</code> to the insert statement.<br>
     * 
     * @param field
     *            The <code>SQLAssignableField</code> to add.
     */
    public void addField(SQLAssignableField field) {
        if (currentEqualsList == null) {
            currentEqualsList = new SQLEqualsList(new SQLEquals(field));
            basisEqualsList = currentEqualsList;
        } else {
            SQLEqualsList tmpField = new SQLEqualsList(new SQLEquals(field));
            currentEqualsList.setConstraint2(tmpField);
            currentEqualsList = tmpField;
        }
    }

    /**
     * Set the value of the assignable constraint with the specified id.
     * 
     * @param id
     *            The id of the assignable constraint to assign value to.
     * @param value
     *            The value to assign.
     * @throws UnknownIdException
     *             Thrown if the specified id is unknown.
     */
    public void setValue(String id, Object value) throws UnknownIdException {
        if (prepared == null) {
            prepare();
        }

        prepared.setValue(id, value);
    }

    /**
     * Convert the <code>SQLUpdate</code> to a prepared statement <code>String</code>.
     * 
     * @return The prepared statement <code>String</code> representation of the <code>SQLUpdate</code> object.
     */
    public String toPreparedStatement() {
        if (prepared == null) {
            prepare();
        }

        return prepared.toPreparedStatement();
    }

    /**
     * Convert the <code>SQLUpdate</code> to a SQL <code>String</code>.
     * 
     * @return The <code>String</code> representation of the <code>SQLUpdate</code> object.
     */
    public String toSQL() {
        if (prepared == null) {
            prepare();
        }

        return prepared.toSQL();
    }

    private void prepare() {
        prepared = new SQLResult();

        prepared.append("UPDATE ");
        prepared.append(table.getTableName());
        prepared.append(" SET ");
        basisEqualsList.prepareSQL(prepared);

        if (basisAndStatement != null) {
            prepared.append(new Dependecy(basisAndStatement));
            basisAndStatement.prepareSQL(prepared);
        }
    }

    private static class Dependecy implements SQLPart {

        private final SQLAnd dependency;

        /* pp */Dependecy(SQLAnd dependency) {
            this.dependency = dependency;
        }

        @Override
        public String toPreparedStatement() {
            return toSQL();
        }

        @Override
        public String toSQL() {
            if (dependency.isAssigned()) {
                return " WHERE ";
            }
            return "";
        }
    }
}